/*******************************************************************************
*   Filename:       bsp_opt300x.c
*   Revised:        All copyrights reserved to Roger.
*   Date:           2020-10-17
*   Revision:       v1.0
*   Writer:	        Roger-WY.
*
*   Description:    TI-opt3001光照度传感器驱动模块
*   Notes:			 http://www.ti.com.cn/product/cn/OPT3001/technicaldocuments
*   All copyrights reserved to Roger-WY
*******************************************************************************/
#include "bsp_opt300x.h"


//----------------------------------------------------------------------------//
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面6行代码即可任意改变SCL和SDA的引脚 */
#define OPT300X_I2C_SCL_RCC()           __HAL_RCC_GPIOA_CLK_ENABLE()
#define OPT300X_I2C_SCL_PIN		        GPIO_PIN_11			/* 连接到SCL时钟线的GPIO */
#define OPT300X_I2C_SCL_PORT            GPIOA

#define OPT300X_I2C_SDA_RCC()           __HAL_RCC_GPIOA_CLK_ENABLE()
#define OPT300X_I2C_SDA_PIN		        GPIO_PIN_12			/* 连接到SDA数据线的GPIO */
#define OPT300X_I2C_SDA_PORT            GPIOA


/* 定义读写SCL和SDA的宏 */
#define OPT300X_I2C_SCL_1()     HAL_GPIO_WritePin(OPT300X_I2C_SCL_PORT,OPT300X_I2C_SCL_PIN,GPIO_PIN_SET)			/* SCL = 1 */
#define OPT300X_I2C_SCL_0()     HAL_GPIO_WritePin(OPT300X_I2C_SCL_PORT,OPT300X_I2C_SCL_PIN,GPIO_PIN_RESET)			/* SCL = 0 */

#define OPT300X_I2C_SDA_1()     HAL_GPIO_WritePin(OPT300X_I2C_SDA_PORT,OPT300X_I2C_SDA_PIN,GPIO_PIN_SET)			/* SDA = 1 */
#define OPT300X_I2C_SDA_0()     HAL_GPIO_WritePin(OPT300X_I2C_SDA_PORT,OPT300X_I2C_SDA_PIN,GPIO_PIN_RESET)			/* SDA = 0 */

#define OPT300X_I2C_SDA_READ()  ((HAL_GPIO_ReadPin(OPT300X_I2C_SDA_PORT, OPT300X_I2C_SDA_PIN)) != 0)	/* 读SDA口线状态 */
#define OPT300X_I2C_SCL_READ()  ((HAL_GPIO_ReadPin(OPT300X_I2C_SCL_PORT, OPT300X_I2C_SCL_PIN)) != 0)	/* 读SCL口线状态 */


//----------------------------------------------------------------------------//



//============================================================================//


/*
********************************************************************************
*	函 数 名: Opt300x_i2c_Delay
*	功能说明: I2C总线位延迟，最快400KHz
*	形    参:  无
*	返 回 值: 无
********************************************************************************
*/
static void Opt300x_i2c_Delay(void)
{
    uint8_t i;

	/*　
    下面的时间是通过逻辑分析仪测试得到的。
    工作条件：CPU主频72MHz ，MDK编译环境，1级优化

    循环次数为10时，SCL频率 = 205KHz
    循环次数为7时，SCL频率 = 347KHz， SCL高电平时间1.5us，SCL低电平时间2.87us
    循环次数为5时，SCL频率 = 421KHz， SCL高电平时间1.25us，SCL低电平时间2.375us
	*/
	for (i = 0; i < 50; i++);
}


static void Opt300x_i2c_Start(void)
{
	/* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
	OPT300X_I2C_SDA_1();
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_1();
	Opt300x_i2c_Delay();

	OPT300X_I2C_SDA_0();
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_0();
//	Opt300x_i2c_Delay();
}

/*******************************************************************************
 * 名    称： i2c_Stop
 * 功    能： CPU发起I2C总线停止信号
 * 入口参数： 无
 * 出口参数： 无
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注： 停止时序
 *            SCL _____/ˉˉˉˉˉˉˉ
 *            SDA _________/ˉˉˉˉˉ
 *                       |   |
 *                       STOP
 *******************************************************************************/
static void Opt300x_i2c_Stop(void)
{
	/* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */

	OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();
    OPT300X_I2C_SDA_0();
    Opt300x_i2c_Delay();

	OPT300X_I2C_SCL_1();
	Opt300x_i2c_Delay();
	OPT300X_I2C_SDA_1();
//	Opt300x_i2c_Delay();
}

/*******************************************************************************
 * 名    称： i2c_WaitAck
 * 功    能： CPU产生一个时钟，并读取器件的ACK应答信号
 * 入口参数： 无
 * 出口参数： 返回0表示正确应答，1表示无器件响应
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注：
 *******************************************************************************/
static uint8_t Opt300x_i2c_WaitAck(void)
{
	uint8_t re = 1;
    uint8_t TimeOutCnt = 20;  /* 超时计数器 */

	OPT300X_I2C_SDA_1();	/* CPU释放SDA总线 */
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	Opt300x_i2c_Delay();

    while(TimeOutCnt -- ) {
        if (OPT300X_I2C_SDA_READ())	{/* CPU读取SDA口线状态 */
            re = 1;
        } else {
            re = 0;
            break;
        }
    }
	OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();
	return re;
}

/*******************************************************************************
 * 名    称： i2c_Ack
 * 功    能： CPU产生一个ACK信号
 * 入口参数： 无
 * 出口参数： 无
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注：
 *******************************************************************************/
static void Opt300x_i2c_Ack(void)
{
	OPT300X_I2C_SDA_0();	/* CPU驱动SDA = 0 */
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_1();	/* CPU产生1个时钟 */
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();
	OPT300X_I2C_SDA_1();	/* CPU释放SDA总线 */
}

/*******************************************************************************
 * 名    称： i2c_NAck
 * 功    能： CPU产生1个NACK信号
 * 入口参数： 无
 * 出口参数： 无
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注：
 *******************************************************************************/
static void Opt300x_i2c_NAck(void)
{
	OPT300X_I2C_SDA_1();	/* CPU驱动SDA = 1 */
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_1();	/* CPU产生1个时钟 */
	Opt300x_i2c_Delay();
	OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();
}

/*******************************************************************************
* 名    称： bsp_InitI2C
* 功    能： 配置I2C总线的GPIO，采用模拟IO的方式实现
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY
* 创建日期： 2018-06-29
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
static void Opt300x_InitI2C(void)
{    
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    OPT300X_I2C_SCL_RCC();
    OPT300X_I2C_SDA_RCC();	/* 打开GPIO时钟 */

    /*Configure GPIO pins */
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull  = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Pin = OPT300X_I2C_SCL_PIN;
    HAL_GPIO_Init(OPT300X_I2C_SCL_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = OPT300X_I2C_SDA_PIN;
    HAL_GPIO_Init(OPT300X_I2C_SDA_PORT, &GPIO_InitStruct);

	/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
	Opt300x_i2c_Stop();
}

/*******************************************************************************
 * 名    称： i2c_SendByte
 * 功    能： CPU向I2C总线设备发送8bit数据
 * 入口参数： _ucByte ： 等待发送的字节
 * 出口参数： 无
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注：
 *******************************************************************************/
static void Opt300x_i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;

	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++) {
        OPT300X_I2C_SCL_0();
		Opt300x_i2c_Delay();

		if (_ucByte & 0x80) {
			OPT300X_I2C_SDA_1();
		} else {
			OPT300X_I2C_SDA_0();
		}

        _ucByte <<= 1;	/* 左移一个bit */

		Opt300x_i2c_Delay();

		OPT300X_I2C_SCL_1();
		Opt300x_i2c_Delay();
	}
    OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();

}

/*******************************************************************************
 * 名    称： Opt300x_i2c_ReadByte
 * 功    能： CPU从I2C总线设备读取8bit数据
 * 入口参数： 无
 * 出口参数： 读到的数据
 * 作　　者： Roger-WY
 * 创建日期： 2018-05-20
 * 修    改：
 * 修改日期：
 * 备    注：
 *******************************************************************************/
static uint8_t Opt300x_i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value;

	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++) {
		value <<= 1;
		OPT300X_I2C_SCL_0();
		Opt300x_i2c_Delay();
        OPT300X_I2C_SCL_1();
		Opt300x_i2c_Delay();

		if (OPT300X_I2C_SDA_READ()) {
			value++;
		}
	}
    OPT300X_I2C_SCL_0();
	Opt300x_i2c_Delay();

	return value;
}

/*******************************************************************************
 * 名    称： bsp_Opt300xWrBytes
 * 功    能： OPT3001写字节
 * 入口参数： 无
* 出口参数： 1:写入成功 0:写入失败
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
uint8_t bsp_Opt300xWrBytes(uint8_t addr,uint8_t reg,uint16_t dat)
{
    uint8_t ret  = 0;
    uint8_t low  = 0;
    uint8_t high = 0;

    low  = (uint8_t)(dat & 0x00FF);
    high = (uint8_t)((dat & 0xFF00) >> 8);

    Opt300x_i2c_Start();
    Opt300x_i2c_SendByte(addr | 0x00); //写入OPT3001的从机地址
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_SendByte(reg);         //写入OPT3001的寄存器地址
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_SendByte(high);         //写入OPT3001的寄存器数据
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_SendByte(low);         //写入OPT3001的寄存器数据
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_Stop();                             //发送停止信号
    return 1;
exit:
    Opt300x_i2c_Stop();                //发送停止信号
    return 0;
}

/*******************************************************************************
 * 名    称： Bsp_OPT3001_RdBytes
 * 功    能： OPT3001读字节
 * 入口参数： 无
 * 出口参数： 1:读出成功 0:读出失败
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
uint8_t bsp_Opt300xRdBytes(uint8_t addr,uint8_t reg,uint16_t *dat)
{
    uint8_t ret = 0;
    uint8_t low  = 0;
    uint8_t high = 0;

    Opt300x_i2c_Start();
    Opt300x_i2c_SendByte(addr | 0x00); //写入OPT3001的从机地址+写指令
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_SendByte(reg);         //写入OPT3001的寄存器地址
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    Opt300x_i2c_Start();
    Opt300x_i2c_SendByte(addr | 0x01); //写入OPT3001的从机地址+读指令
    ret = Opt300x_i2c_WaitAck();
    if(ret) {
        goto exit;                  //器件无响应
    }
    high = Opt300x_i2c_ReadByte();     //读OPT3001的寄存器高字节
    Opt300x_i2c_Ack();            //主机给OPT3001一个应答信号
    low = Opt300x_i2c_ReadByte();      //读OPT3001的寄存器低字节
    Opt300x_i2c_NAck();            //主机给OPT3001一个非应答信号
    Opt300x_i2c_Stop();                             //发送停止信号
    
    *dat =  (high << 8) + low;
    return 1;
exit:
    Opt300x_i2c_Stop();                             //发送停止信号
    return 0;
}

/*******************************************************************************
 * 名    称： Bsp_Opt3001CheckId
 * 功    能： 读取OPT3001厂商ID和设备ID
 * 入口参数： 无
 * 出口参数： 1:读取ID成功 0:读取ID失败
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
uint8_t bsp_Opt300xCheckId(uint8_t addr)
{
    uint16_t id = 0;
    bsp_Opt300xRdBytes(addr,OPT3001_MANUFACTURER_ID,&id);
    bsp_Opt300xRdBytes(addr,OPT3001_DEVICE_ID,&id);
    if(id == OPT3001_DEVICE_ID_VAL)
    {
        return 1;
    }
    return 0;
}


/*******************************************************************************
 * 名    称： Bsp_Opt3001Init
 * 功    能： OPT3001初始化
 * 入口参数： 无
 * 出口参数： 无
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
int8_t bsp_Opt300xInit(void)
{
    int8_t ret = -1;
    
    
    Opt300x_InitI2C();
    if(bsp_Opt300xCheckId(OPT3001_ADDRESS_GND) == 1)
    {
        //可在此处配置OPT3001的配置寄存器
        bsp_Opt300xConfig(OPT3001_ADDRESS_GND);
        ret = 0;
    }
    else
    {
        ret = -1;
    }

    return ret ;
}

/*******************************************************************************
 * 名    称： Bsp_Opt3001Config
 * 功    能： OPT3001写入配置
 * 入口参数： addr：I2C从机地址
* 出口参数： 1:写入成功 0:写入失败
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
int8_t bsp_Opt300xConfig(uint8_t addr)
{
    int8_t   ret  = 0;
    uint16_t vCfg = 0;
    //------------------------------------------------------------------------//
    //12:15 RN  	- 配置测量光照的范围 见手册20页表9  当配置位1100传感器测量范围自动选择
    //11    CT  	- 测量时间配置 0- 100Ms  1-800Ms
    //10:9  M[1:0]	- 转换模式 00-关闭模式  01 - 单次转换  10、11 - 连续多次转换
    //8     OVF     - 测量光照超出设定的范围或最大测量值 溢出标志
    //7     CRF		- 转换就绪字段 1-转换完成
    //6     FH		- 转换的光照值 大于上限值 置位
    //5     FL		- 转换的光照值 小于下限值 置位
    //4     L		- 中断输出的两种模式  1-窗口模式 这种模式下高限置位和低限置位INT输出  0-滞后模式 高限置位INT输出 具体看手册
    //3     POL		- INT 中断被触发输出极性 0-拉低  1-拉高
    //2     ME 		- 掩码字段
    //0:1   FC		- 超出上限范围故障计数  如果超出次数 大于等于计数设定次数  INT输出中断
    //------------------------------------------------------------------------//
    //自动测量范围
    vCfg |= OPT3001_CONFIGURATION_RN_AUTO;
    //测量时间 800ms	
    vCfg |= OPT3001_CONFIGURATION_CT;
    //连续多次转换
    vCfg |= OPT3001_CONFIGURATION_M_CONTINUOUS;
    //窗口模式
    vCfg |= OPT3001_CONFIGURATION_L;

    //将配置写入配置寄存器
    ret = bsp_Opt300xWrBytes(addr,OPT3001_CONFIGURATION, vCfg);

    return (ret);
}

/*******************************************************************************
 * 名    称： Bsp_ReadOpt3001Lux
 * 功    能： 获取光照度
 * 入口参数： 无
 * 出口参数： 无
 * 作　　者： Roger.WY
 * 创建日期： 2019-09-03
 * 修    改：
 * 修改日期：
 * 备    注：
 ******************************************************************************/
int8_t bsp_ReadOpt300xLux(uint8_t addr,float *lux)
{
    uint8_t     ret = -1;
    uint16_t    readlux = 0;

    uint16_t vDatE = 0;
    uint16_t vDatR = 0;

    float vFval     = 0;
    float vLsbSize  = 0;
    float vFlux     = 0;

    if(bsp_Opt300xRdBytes(addr,OPT3001_RESULT,&readlux) == 1) {
        vDatE = OPT3001_REG_EXPONENT(readlux);
        vDatR = OPT3001_REG_MANTISSA(readlux);

        vFval = (0x01 << vDatE);
        vLsbSize = (0.01 * vFval);
        vFlux = (vLsbSize * (float)vDatR);

        //放大100倍
        //*lux =  (uint16_t)((vFlux)*100.0f);//透明外壳不需要矫正 ，乳白色外壳推荐*1.8矫正 *vp_Lux = ((vFlux*1.8)*100.0)
        //正常值
        *lux =  (vFlux);
        ret = 0;
    } else {
       *lux = 0;      //光照度采集失败
        ret = -1;
    }
    return ret;
}

/***************************** (END OF FILE) **********************************/
